`timescale 1ns/100ps
`default_nettype none

/***********************************************
 * Load Pre-IDCT (S Prime) 
 * from SRAM 
 * to dual port RAM
 ***********************************************/
module SPLoad (
    input logic CLOCK_50_I,             // 50 MHz clock
    input logic resetn,                       // Master reset
    input logic clearResult,            // clear result from previous run
    input logic preIDCTYDone_in,
    
    input logic [31:0] DP_Read,         // Dual port RAM read data
    input logic [15:0] SRAM_Read,       // SRAM read data
    input logic [17:0] SRAM_BaseAddr,   // SRAM base address for start writing

    input logic start,                  // Module start signal

    output logic [6:0] DP_Addr,         // Dual port RAM address line
    output logic [31:0] DP_Write,       // Dual port RAM write data
    output logic DP_we,                 // Dual port RAM write enable

    output logic [17:0] SRAM_Addr,      // SRAM address line

    output logic finish                 // Module finish indication
);

logic preIDCTYDone;   // Indicate if preIDCT Y is done reading when we start
logic [17:0] baseOffset;    // baseOffset for each row
logic [2:0] row;      // Row offset, from 0 to 7
logic [2:0] col;      // Column offset, from 0 to 7

enum logic [3:0] {
    IDLE,
    R_WAIT0,
    R_WAIT1,
    READ,
    R_DELAY0,
    R_DELAY1
} state;

// Provide sign extension for values written to DP_RAM
assign DP_Write[15:0] = SRAM_Read;
assign DP_Write[31:16] = {SRAM_Read[15], SRAM_Read[15], SRAM_Read[15], SRAM_Read[15], SRAM_Read[15], SRAM_Read[15], SRAM_Read[15], SRAM_Read[15], SRAM_Read[15], SRAM_Read[15], SRAM_Read[15], SRAM_Read[15], SRAM_Read[15], SRAM_Read[15], SRAM_Read[15], SRAM_Read[15]};

assign SRAM_Addr = baseOffset + col;  // SRAM Address should always be baseOffset + colum

always_ff @ (posedge CLOCK_50_I or negedge resetn) begin
    if (resetn == 1'b0) begin
        preIDCTYDone <= 1'b0;
        DP_Addr <= 7'b0;
        DP_we <= 1'b0;

        baseOffset <= SRAM_BaseAddr;
        row <= 3'b0;
        col <= 3'b0;

        finish <= 1'b0;

        state <= IDLE;
    end else begin
        case (state)
        IDLE: begin
            if (start) begin
                // Initialize variables
                preIDCTYDone <= preIDCTYDone_in;
                DP_Addr <= 7'b0;
                DP_we <= 1'b0;

                baseOffset <= SRAM_BaseAddr;
                row <= 3'b0;
                col <= 3'b0;

                finish <= 1'b0;

                state <= R_WAIT0;
            end else if (clearResult) begin
                // Initialize variables
                DP_Addr <= 7'b0;
                DP_we <= 1'b0;

                baseOffset <= SRAM_BaseAddr;
                row <= 3'b0;
                col <= 3'b0;

                finish <= 1'b0;
            end
        end
        R_WAIT0: begin
            // SRAM Address stable, requesting 1st SRAM location
            // Advance column counter
            col <= col + 3'b1;

            state <= R_WAIT1;
        end
        R_WAIT1: begin
            // SRAM Address stable, requesting 2nd SRAM location
            
            // SRAM read data will be stable for first time at next cycle.
            //  We need to setup DP_we to prepare for the write
            DP_we <= 1'b1;
            
            // Advance column counter
            col <= col + 3'b1;
            
            state <= READ;
        end
        READ: begin
            // SRAM Address stable, requesting next SRAM location.
            
            // SRAM read data stable for first time
            //  We enabled DP_we last clock, so it will be written to first
            //  location of DP_Addr

            // Advance DP_Addr for next write
            DP_Addr <= DP_Addr + 7'b1;

            // Logic to steer column and row counter respectively
            if ( col== 3'd7 ) begin
                col <= 3'b0;        // Reset column counter
                if (preIDCTYDone==1'b0) begin
                    // Advance baseOffset by 1 row
                    baseOffset <= baseOffset + 18'd160;
                end else begin
                    // Advance baseOffset by 1 row of U and V
                    baseOffset <= baseOffset + 18'd80;
                end
                // If both column and row count is 7, then we are at last
                // location of 8x8 block. Go to R_DELAY to wait for SRAM
                // finish reading
                if ( row==3'd7 ) begin
                    state <= R_DELAY0;
                end else begin
                    row <= row + 3'b1;  // Advance row counter
                end
            end else begin
                // Advance column counter
                col <= col + 3'b1;
            end
        end
        R_DELAY0: begin
            // 2nd last SRAM read data becomes stable, put it into DP RAM

            // Advance DP_Addr for next write
            DP_Addr <= DP_Addr + 7'b1;
            
            state <= R_DELAY1;
        end
        R_DELAY1: begin
            // 2nd last SRAM read data becomes stable, put it into DP RAM
            
            // Unset DP_we
            DP_we <= 1'b0;

            // We are done, will there be cake?
            finish <= 1'b1;

            state <= IDLE;
        end
        default: begin
            state <= IDLE;
        end
        endcase
    end
end

endmodule
